package edu.luc.cs.backend;
//%TITLE   Date class              (Copyright 1997, Information Disciplines, Inc.)
                                                      //package ididates;
public class Date {

//  NOTE:  The class name duplicates the name of a class in "java.util".  While
//  we normally avoid conflicts with library components, that misnamed class is
//  so poorly conceived and user-hostile that we would never use it in any IDI
//  application, and we strongly recommend against using it anywhere.
//%SPACE 4
//  Internal representation
//  -----------------------

  int  value;                          //  Number of days since origin

  static final int bias                // Origin date is December 30, 1899,
   = - CalendarInfo.DAYS_PER_400_YEARS * 5 //  for compatibility with Lotus
     + CalendarInfo.DAYS_PER_100_YEARS     //  1-2-3 and various other 
     + CalendarInfo.DAYS_PER_YEAR + 2;     //  software products

  static final short bias_weekday = 6; // Origin date was a Saturday
//%SPACE 2
//  Note that:

//    1.  Some conversion functions assume the Gregorian calendar,
//        even for dates before that calendar was adopted.

//    2.  B.C. dates can be generated by arithmetic operations, but
//        are not necessarily supported by conversion functions.
//%EJECT
//  Constructors
//  ------------

 public Date(int yyyy, int ddd)            //  Year and day number
 {int  years = yyyy - 1;
  value = bias + ddd                       //  Start with base date
      + years * CalendarInfo.DAYS_PER_YEAR //  Convert years to days
      + years/4 - years/100 + years/400;  //  Apply leap year adjustments
 }
//%SPACE 2
 public Date(int yyyy, int mm, int dd)     //  Year, month, and day of month
 {this(yyyy,CalendarInfo.dayNumber(yyyy,mm,dd));}
//%SPACE 2
 public Date(Date d) {value = d.value;}    //  Copy constructor
//%SPACE 2
 public Date() {}                          //  No default value
//%SPACE 2
 public Date(String s)                     //  YYMMDD (ANSI character repr.)
 {String yymmdd = new String(s.trim());    //    Result is undefined if illegal
  if (yymmdd.length() != 6) {return;}
  char charVal[] = new char[6];            //  Numeric value of each character
  for (int i = 0; i < 6; i++)		   //  Decompose string to into 
      charVal[i] = (char) (yymmdd.charAt(i) - '0'); //  character values
  int y = charVal[0] * 10 + charVal[1];	    	
  int m = charVal[2] * 10 + charVal[3];
  int d = charVal[4] * 10 + charVal[5];
  this.set(new Date(y+(y<CalendarInfo.centuryBreak ?
                                2000 : 1900), m, d));
 }
//%SPACE 3
//  Pseudo assignment operator
//  --------------------------

 public Date set(Date rs) {value = rs.value; return this;}
//%EJECT
//  Accessors
//  ---------

 public short weekday()
       {return (short)(((value + bias_weekday) % 7 + 7) % 7);}

//  To avoid redundant computation and still provide an independent accessors
//  for each component, the private decomposition function, ymd, caches its
//  result.  If the user then invokes more accessors for the same date before
//  invoking any for a different date, the saved values are retrieved.

 public synchronized int   year () {ymd(); return y;}
 public synchronized byte  month() {ymd(); return m;}
 public synchronized byte  day  () {ymd(); return d;}
 public synchronized int   dayno() {ymd(); return ddd;}

 static int   y;                  //  Set by ymd function (below) and
 static byte  m, d;               //    retrieved by accessors (above)
 static int   ddd;
 static int   cur_value = bias;   //  Flag to lock above values
//%EJECT
//  Decompose a date into calendar components (See above accessors)
//  -----------------------------------------

synchronized void ymd()
{if (value == cur_value) return;  //  Do nothing if unchanged
  cur_value = value;              //  Save value for next time
  int ngrps;
  
  ddd    = value - bias;          //  Strip off origin date

  ngrps  = ddd   / CalendarInfo.DAYS_PER_400_YEARS;
  y      = ngrps * 400;
  ddd   -= ngrps * CalendarInfo.DAYS_PER_400_YEARS;

  ngrps  = ddd   / CalendarInfo.DAYS_PER_100_YEARS;
  y     += ngrps * 100;
  ddd   -= ngrps * CalendarInfo.DAYS_PER_100_YEARS;

  ngrps  = ddd / CalendarInfo.DAYS_PER_4_YEARS;
  y     += ngrps * 4;
  ddd   -= ngrps * CalendarInfo.DAYS_PER_4_YEARS;


  if (ddd == 0)                              //  End-of-year correction
        ddd=CalendarInfo.isLeapYear(y) ? 366 : 365;              
  else {ddd  += CalendarInfo.DAYS_PER_YEAR - 1;
        y    += ddd / CalendarInfo.DAYS_PER_YEAR;
        ddd  %= CalendarInfo.DAYS_PER_YEAR; ++ddd;
      }

//  At this point y is the year and ddd is the day number 

  int dx = ddd;                       //  (Leap-year corrected day number)  
  if (CalendarInfo.isLeapYear(y))     //  Handle February 29 as special case
      if (ddd > 60) --dx;             //  Adjust day number
      else if (ddd == 60) {m = 2; d = 29; return;}

  m = (byte)((dx + 28) / 29);    //  Estimate the month, then adjust
  if  (dx <= CalendarInfo.DAYS_BEFORE_MONTH[m]) m--;

  d = (byte)(dx  - CalendarInfo.DAYS_BEFORE_MONTH[m]);
  if  (m == 13) {m = 1; y++;}

 }
//%EJECT
//  Conversion functions
//  --------------------

 public String toString()           //  Default external representation
 {String mm=new String(((month() < 10) ? "0":"") + month()); // Insert
  String dd=new String(((day  () < 10) ? "0":"") + day  ()); //   leading zero
  return year() + "-" + mm + '-' + dd;}

 public String toEnglish()          //  Standard English (non-American)
 {return day() + " " + CalendarInfo.MONTH_NAME[month()] + " " + year();}
//%SPACE 4
//************ NOTE:
//   From here on Java demands repetition of code which in C++ we would
//   package for reuse.  Although this imposes major maintenance and
//   testing burdens, Java offers no practical solution as of March, 1997.
//%SPACE 2
//  Relational operators  (implements "ordered")
//  --------------------

 public boolean equals     (Date rs) {return value == rs.value;}
 public boolean lessThan   (Date rs) {return value <  rs.value;}
 public boolean greaterThan(Date rs) {return value >  rs.value;}
//%SPACE 2
//  Arithmetic operators  (implements "point")
//  --------------------

 public Date add  (Days rs)  {return new Date(this).addSet(rs);}
 public Date sub  (Days rs)  {return new Date(this).subSet(rs);}
 public Days sub  (Date rs)  {return new Days(value - rs.value);}
 public Date add  (int  rs)  {return new Date(this).addSet(rs);}
 public Date sub  (int  rs)  {return new Date(this).subSet(rs);}

 public Date addSet(Days rs)  {value += rs.toInt(); return this;}
 public Date subSet(Days rs)  {value -= rs.toInt(); return this;}
 public Date addSet(int  rs)  {value += rs;         return this;}
 public Date subSet(int  rs)  {value -= rs;         return this;}
//%SPACE 2
// Special function to get the current date 
// ---------------- (The only method that uses Java library standard class)
 public static Date today()
    {java.util.GregorianCalendar d = new java.util.GregorianCalendar();
     return new Date(d.get(java.util.Calendar.YEAR),
                     d.get(java.util.Calendar.MONTH)+1,
                     d.get(java.util.Calendar.DATE));
    }

}
